package com.jx.service.statistics;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jx.common.util.DateUtil;
import com.jx.common.util.ObjUtil;
import com.jx.dao.statistics.ArticleVisitsStatDao;
import com.jx.dao.statistics.PvStatDao;
import com.jx.dao.statistics.UvStatDao;
import com.jx.pojo.entity.statistics.ArticleVisitsStat;
import com.jx.pojo.entity.statistics.PvStat;
import com.jx.pojo.entity.statistics.UvStat;
import com.jx.pojo.po.content.ItemPO;
import com.jx.pojo.po.statistics.ArticleVisitsPO;
import com.jx.pojo.po.statistics.PvStatTrendPO;
import com.jx.pojo.vo.statistics.StatisticsVO;
import com.jx.service.cache.RedisCache;
import com.jx.service.content.ArticleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StatisticsService {

    @Resource
    private RedisCache redisCache;

    @Resource
    private UvStatDao uvStatDao;

    @Resource
    private PvStatDao pvStatDao;

    @Resource
    private ArticleService articleService;

    @Resource
    private ArticleVisitsStatDao articleVisitsStatDao;

    /**
     * 每天的PV量
     */
    public static final String PV_DAY_KEY = "statistics:pv:day:";

    /**
     * 每日UV统计
     */
    public static final String UV_DAY_KEY = "statistics:uv:day:";

    /**
     * 日期格式
     */
    public static final String Date_FORMAT = "yyyy-MM-dd";

    /**
     * 更新流量
     */
    public void updateTraffic(StatisticsVO statisticsVO) {
        String pageUrl = statisticsVO.getPageUrl();
        String uniqueId = statisticsVO.getUniqueId();

        // 统计pv
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern(Date_FORMAT));
        Object pvCount = redisCache.getHashKey(PV_DAY_KEY + dateStr, pageUrl);
        redisCache.setHash(PV_DAY_KEY + dateStr, pageUrl, pvCount != null ? Integer.parseInt(pvCount.toString()) + 1 : 1);

        // 统计UV，同一用户一天访问多次只算一次
        redisCache.setSet(UV_DAY_KEY + dateStr, uniqueId);
    }

    /**
     * 同步pv缓存数据到db
     */
    public void syncPVCacheToDb() {
        // 查询前一日pv缓存数据
        LocalDate localDate = LocalDate.now().plusDays(-1);
        String dateStr = localDate.format(DateTimeFormatter.ofPattern(Date_FORMAT));
        Map<Object, Object> data = redisCache.getHash(PV_DAY_KEY + dateStr);
        List<PvStat> list = new ArrayList<>();
        Set<Map.Entry<Object, Object>> entries = data.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            PvStat pvStat = new PvStat();
            pvStat.setPageUrl(ObjUtil.covToString(entry.getKey()));
            pvStat.setStatNum(ObjUtil.covToInt(entry.getValue()));
            pvStat.setStatDate(localDate);
            list.add(pvStat);
        }
        pvStatDao.insert(list);
    }

    /**
     * 同步uv缓存数据到db
     */
    public void syncUVCacheToDb() {
        // 查询前一日uv缓存数据
        LocalDate localDate = LocalDate.now().plusDays(-1);
        String dateStr = localDate.format(DateTimeFormatter.ofPattern(Date_FORMAT));
        Set<Object> data = redisCache.getSet(UV_DAY_KEY + dateStr);
        UvStat uvStat = new UvStat();
        uvStat.setStatDate(localDate);
        uvStat.setStatNum(data.size());
        uvStatDao.insert(uvStat);
    }

    /**
     * 获取pv总数
     */
    public Long getPVCount() {
        int cacheCount = getTodayPv();
        QueryWrapper<PvStat> wrapper = new QueryWrapper<>();
        wrapper.select("sum(stat_num) as sumTotal");
        PvStat pvStat = pvStatDao.selectOne(wrapper);
        long dbCount = pvStat != null ? pvStat.getSumTotal() : 0L;
        return dbCount + cacheCount;
    }

    /**
     * 获取今日pv数量
     */
    private int getTodayPv() {
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern(Date_FORMAT));
        Map<Object, Object> data = redisCache.getHash(PV_DAY_KEY + dateStr);
        Collection<Object> values = data.values();
        int cacheCount = 0;
        for (Object value : values) {
            cacheCount += ObjUtil.covToInt(value);
        }
        return cacheCount;
    }

    /**
     * 获取uv总数
     */
    public Integer getUVCount() {
        int cacheCount = getTodayUv();

        UvStat uvStat = uvStatDao.selectOne(null);
        int dbCount = uvStat != null ? uvStat.getStatNum() : 0;
        return dbCount + cacheCount;
    }

    /**
     * 获取今日uv数量
     */
    private int getTodayUv() {
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern(Date_FORMAT));
        Set<Object> set = redisCache.getSet(UV_DAY_KEY + dateStr);
        return set != null ? set.size() : 0;
    }

    /**
     * 统计pv和uv的变化趋势
     */
    public Map<String, Object> countPvAndUv() {

        // pv数据
        LocalDate startLocalDate = LocalDate.now().plusDays(-7);
        LocalDate endLocalDate = LocalDate.now().plusDays(-1);
        DateTimeFormatter pattern = DateTimeFormatter.ofPattern(Date_FORMAT);
        String startDate = startLocalDate.format(pattern);
        String endDate = endLocalDate.format(pattern);
        List<PvStatTrendPO> list = pvStatDao.countByStatDate(startDate, endDate);
        Map<String, Integer> pvDataMap = list.stream().collect(Collectors.toMap(PvStatTrendPO::getStatDate, PvStatTrendPO::getTotal));
        int todayPv = getTodayPv();
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern(Date_FORMAT));
        pvDataMap.put(dateStr, todayPv);

        // uv数据
        QueryWrapper<UvStat> wrapper = new QueryWrapper<>();
        wrapper.between("stat_date", startDate, endDate);
        List<UvStat> uvStats = uvStatDao.selectList(wrapper);
        Map<String, Integer> uvDataMap = uvStats.stream().collect(Collectors.toMap(s -> s.getStatDate().toString(), UvStat::getStatNum, (oldValue, newValue) -> newValue));
        int todayUv = getTodayUv();
        uvDataMap.put(dateStr, todayUv);

        List<String> dateList = DateUtil.getDayListWithinOneWeek();
        List<List<Integer>> countListAll = new ArrayList<>();
        List<Integer> pvCountList = new ArrayList<>();
        List<Integer> uvCountList = new ArrayList<>();
        for (String date : dateList) {
            Integer pvCount = pvDataMap.getOrDefault(date, 0);
            pvCountList.add(pvCount);
            Integer uvCount = uvDataMap.getOrDefault(date, 0);
            uvCountList.add(uvCount);
        }
        countListAll.add(pvCountList);
        countListAll.add(uvCountList);

        Map<String, Object> result = new HashMap<>();
        result.put("date", dateList);
        result.put("count", countListAll);
        result.put("type", Arrays.asList("pv", "uv"));
        return result;
    }

    /**
     * 获取文章类别Top10
     */
    public List<ItemPO> getArticleCategoryTopN(Integer n) {
        return articleService.getArticleCategoryTopN(n == null ? 10 : n);
    }

    /**
     * 文章访问量+1
     */
    public void increaseArticleVisits(StatisticsVO statisticsVO) {
        Integer articleId = statisticsVO.getArticleId();
        if (articleId == null) {
            return;
        }
        articleVisitsStatDao.increaseArticleVisits(articleId);
    }

    /**
     * 统计文章访问量
     */
    public Map<String, Object> countArticleVisits(Integer n) {
        List<ArticleVisitsPO> poList = articleVisitsStatDao.countArticleVisits(n == null ? 5 : n);
        List<String> titleList = new ArrayList<>();
        List<Integer> countList = new ArrayList<>();
        for (ArticleVisitsPO po : poList) {
            titleList.add(po.getTitle());
            countList.add(po.getVisits());
        }
        Map<String, Object> result = new HashMap<>();
        result.put("titleList", titleList);
        result.put("countList", countList);
        return result;
    }

}
